/*
 * main.c
 *
 * Implements an object oriented pascal compiler
 */

#include "shared.h"
#include "symtab.h"
#include "rulefuncs.h"
#include "semantic.h"
#include "ir_map_pascal.h"
#include "ir_print.h"
#include "opt_constant_folding.h"
#include "opt_redundant_elimination.h"
#include "opt_value_numbering.h"
#include "opt_global_redundancy_elimination.h"
#include "gen.h"


/* program name */
char progName [256];

/* Flags if any errors occured */
int error_flag = 0;

/* Holds the command-line arguments */
struct args_t cmdArgs;

/* This is the data structure we are left with (the parse tree) after
   yyparse() is done . */
extern struct program_t *program;

extern void yyparse();

char * getMainFuncName ()
    {
    return progName;
    }
/* -----------------------------------------------------------------------
 * Printout on error message and exit
 * ----------------------------------------------------------------------- 
 */
void exit_on_errors()
{
  if (error_flag == 1) {
    printf("Errors detected. Exiting.\n");
    exit(-1);
  }
}



/* ----------------------------------------------------------------------- 
 * Prints a usage banner
 * ----------------------------------------------------------------------- 
 */
void print_usage()
{
  printf("%s: Usage: %s [-v] [-exitsem] [-O] [-ir] [-free] [-fcf] [-fvn] [-fgre] [--help]\n",
	  PROGRAMNAME, PROGRAMNAME);
}


/* ----------------------------------------------------------------------- 
 * parses the command-line arguments to main
 * ----------------------------------------------------------------------- 
 */
void parse_command_line_arguments(int argc, char **argv, struct args_t *args)
{
  int i = 1;

  if (argc < 1) {
    print_usage();
    exit(1);
  }

  memset(args, 0, sizeof(struct args_t));

  while (i < argc) {
    if (strcmp(argv[i], "-v") == 0 ) {
      args->verbose = 1;
    } else if (strcmp(argv[i], "--help") == 0 ||
	       strcmp(argv[i], "-h") == 0 ) { 
      print_usage();
      exit(3);
    } else if (strcmp(argv[i], "-exitsem") == 0) {
      args->exit_after_sem = 1;
    } else if (strcmp(argv[i], "-ir") == 0) {
      args->printIr = 1;
    } else if (strcmp(argv[i], "-lir") == 0) {
      args->printLir = 1;
    } else if (strcmp(argv[i], "-fcf") == 0) {
      args->optimize_cf = 1;
    } else if (strcmp(argv[i], "-free") == 0) {
      args->optimize_ree = 1;
    } else if (strcmp(argv[i], "-fvn") == 0) {
      args->optimize_vn = 1;
    } else if (strcmp(argv[i], "-fgre") == 0) {
      args->optimize_gre = 1;
    } else if (strcmp(argv[i], "-O") == 0) {
      args->optimize_cf = 1;
      args->optimize_ree = 1;
      args->optimize_vn = 1;
      args->optimize_gre = 1;
    } else {
      fprintf(stderr, "Invalid argument '%s'\n", argv[i]);
      print_usage();
      exit(1);
    }
    i++;
  }

}



/* -----------------------------------------------------------------------
 * main function, where all hell breaks loose
 * ----------------------------------------------------------------------- 
 */
int main(int argc, char **argv)
{
  parse_command_line_arguments(argc, argv, &cmdArgs);

  /* Initialize the code generation backend */
  gen_init();

  {
    struct usrdef_t *u;

    usrdef_init();
    symtab_init();

    /* Add the primitive data types.

       Why aren't these in usrdef_init ? Because the table of user
       defined datatypes is a generic implementation that can be used
       by other frontends, not just objective Pascal, that will need
       to initialize themselves differently */
    u = usrdef_insert(PRIMITIVE_TYPE_NAME_INTEGER, -1);
    u->td = new_type_denoter();
    u->td->type = TYPE_DENOTER_T_IDENTIFIER;
    u->size = gen_primitive_size(PRIMITIVE_TYPE_NAME_INTEGER);

    u = usrdef_insert(PRIMITIVE_TYPE_NAME_BOOLEAN, -1);
    u->td = new_type_denoter();
    u->td->type = TYPE_DENOTER_T_IDENTIFIER;
    u->size = gen_primitive_size(PRIMITIVE_TYPE_NAME_BOOLEAN);
  }

  /* begin parsing */
  yyparse();

  /* If there were parsing errors, exit. */
  exit_on_errors();

  /* Perform semantic analysis */
  semantic_analysis(program);
  strcpy (progName, program->ph->id);
  /* If there were errors during semantic analysis, exit. */
  exit_on_errors();

  /* If we should only perform semantic analysis, exit */
  if (cmdArgs.exit_after_sem == 1) {
    exit(0);
  }

        gen_compute_offsets();
  
  {
    struct ir_list_t *ir;

    /* Create an intermediate representation. */
    ir = ir_map_pascal_create(program);

    /* Apply optimizations */
    {
      if (cmdArgs.optimize_cf == 1)
	opt_constant_folding(ir);

      if (cmdArgs.optimize_vn == 1)
	opt_value_numbering_list(ir);
      
      if (cmdArgs.optimize_gre == 1)
	opt_global_redundancy_elimination(ir);
      
      if (cmdArgs.optimize_ree == 1)
	opt_redundant_elimination(ir);
      
      if (cmdArgs.printIr == 1)
          ir_print(ir);
    }

    /* Generate code */
    gen(ir);
  }

  if (cmdArgs.verbose == 1) {
    /* print the user defined data types */
    printf("USER DEFINED DATA TYPES:\n");
    printf("------------------------\n");
    usrdef_print();
    
    /* print the symbol table*/
    printf("\n\n");
    printf("SYMBOL TABLE:\n");
    printf("-------------\n");
    symtab_print(0);
  }

  /* generate mips code */
//  generate_mips_code (ir) ;

  /* Simple, wasn't it ?!! */

  return 0;
}
